package com.linkoog.devtools.project_manager.everywhere;

import com.intellij.CommonBundle;
import com.intellij.execution.runners.ExecutionUtil;
import com.intellij.ide.IdeBundle;
import com.intellij.ide.IdeEventQueue;
import com.intellij.ide.RecentProjectsManager;
import com.intellij.ide.RecentProjectsManagerBase;
import com.intellij.ide.actions.CloseProjectsActionBase;
import com.intellij.ide.actions.SearchEverywherePsiRenderer;
import com.intellij.ide.actions.searcheverywhere.FoundItemDescriptor;
import com.intellij.ide.actions.searcheverywhere.SearchEverywhereContributor;
import com.intellij.ide.actions.searcheverywhere.SearchEverywhereContributorFactory;
import com.intellij.ide.actions.searcheverywhere.SearchEverywhereManager;
import com.intellij.ide.actions.searcheverywhere.SearchEverywhereUI;
import com.intellij.ide.actions.searcheverywhere.WeightedSearchEverywhereContributor;
import com.intellij.ide.impl.OpenProjectTask;
import com.intellij.ide.impl.ProjectUtil;
import com.intellij.ide.startup.StartupManagerEx;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ModalityState;
import com.intellij.openapi.editor.markup.TextAttributes;
import com.intellij.openapi.fileChooser.impl.FileChooserUtil;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.wm.impl.ProjectWindowAction;
import com.intellij.openapi.wm.impl.ProjectWindowActionGroup;
import com.intellij.psi.codeStyle.MinusculeMatcher;
import com.intellij.psi.codeStyle.NameUtil;
import com.intellij.ui.ColoredListCellRenderer;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.ui.speedSearch.SpeedSearchUtil;
import com.intellij.util.ModalityUiUtil;
import com.intellij.util.Processor;
import com.intellij.util.ui.UIUtil;
import com.linkoog.devtools.project_manager.settings.ProjectManagerSetting;
import com.linkoog.devtools.project_manager.settings.domain.ProjectDirectory;
import com.linkoog.devtools.project_manager.settings.domain.ProjectInfo;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.stream.Collectors;

public class ProjectManagerEverywhereContributor implements WeightedSearchEverywhereContributor<ProjectManagerNavigationItem> {

    protected final Project myProject;
    final AnActionEvent event;
    private List<FoundItemDescriptor<ProjectManagerNavigationItem>> foundItemDescriptorList;

    public static final String SEARCH_PROVIDER_ID = "ProjectSwitcher";
    
    public ProjectManagerEverywhereContributor(@NotNull final AnActionEvent event) {
        myProject = event.getRequiredData(CommonDataKeys.PROJECT);
        this.event = event;
    }
    
    @NotNull
    public String getSearchProviderId() {
        return SEARCH_PROVIDER_ID;
    }
    
    @NotNull
    public String getGroupName() {
        return "Projects";
    }
    
    public int getSortWeight() {
        return 1;
    }
    
    @NotNull
    public ListCellRenderer<Object> getElementsRenderer() {
        return new SearchEverywherePsiRenderer(this) {
            private JList list = null;
            private final KeyAdapter keyAdapter = new KeyAdapter() {
                @Override
                public void keyPressed(final KeyEvent e) {
                    if (e.getKeyLocation() != KeyEvent.KEY_LOCATION_LEFT && e.getKeyLocation() != KeyEvent.KEY_LOCATION_RIGHT) {
                        return;
                    }
                    if (e.getKeyCode() == KeyEvent.VK_DELETE && list != null) {
                        final Object selectedValue = list.getSelectedValue();
                        if (!(selectedValue instanceof ProjectManagerNavigationItem)) {
                            return;
                        }
                        ProjectManagerEverywhereContributor.closeProject((ProjectManagerNavigationItem)selectedValue, ProjectManagerEverywhereContributor.this.event);
                    }
                }
            };
            
            public Component getListCellRendererComponent(final JList list, final Object value, final int index, final boolean isSelected, final boolean cellHasFocus) {
                final Component retComponent = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
                if (index == 0 && this.list == null) {
                    this.list = list;
                    try {
                        final Component[] components = list.getParent().getParent().getParent().getParent().getParent().getComponents();
                        final Component component = components[0];
                        if (component instanceof SearchEverywhereUI) {
                            final SearchEverywhereUI everywhereUI = (SearchEverywhereUI)component;
                            //everywhereUI.getSearchField().addKeyListener(this.keyAdapter);
                        }
                    } catch (Exception ex) {
                    }
                }
                return retComponent;
            }
            
            protected boolean customizeNonPsiElementLeftRenderer(final ColoredListCellRenderer renderer, final JList list, final Object value, final int index, final boolean selected, final boolean hasFocus) {
                final Color fgColor = list.getForeground();
                final Color bgColor = UIUtil.getListBackground();
                final TextAttributes attributes = this.getNavigationItemAttributes(value);
                SimpleTextAttributes nameAttributes = (attributes != null) ? SimpleTextAttributes.fromTextAttributes(attributes) : null;
                if (nameAttributes == null) {
                    nameAttributes = new SimpleTextAttributes(0, fgColor);
                }
                final ItemMatchers itemMatchers = this.getItemMatchers(list, value);
                final ProjectManagerNavigationItem item = (ProjectManagerNavigationItem)value;
                final String name = item.getProjectName() + " ";

                String locationString;
                if (item.isOpen()){
                    locationString = "(opened)";
                } else {
                    String userHome = System.getProperty("user.home");
                    if (item.getProjectPath().startsWith(userHome)){
                        Path homePath = Paths.get(userHome);
                        Path path = Paths.get(item.getProjectPath());
                        locationString =  "~/" +  homePath.relativize(path);
                    } else {
                        locationString = item.getProjectPath();
                    }
                }

                SpeedSearchUtil.appendColoredFragmentForMatcher(name, renderer, nameAttributes, itemMatchers.nameMatcher, bgColor, selected);
                renderer.setIcon(item.isOpen() ? ExecutionUtil.getLiveIndicator(item.getIcon()) : item.getIcon());
                final FontMetrics fm = list.getFontMetrics(list.getFont());
                final int maxWidth = list.getWidth() - fm.stringWidth(name) - this.myRightComponentWidth - 36;
                final int fullWidth = fm.stringWidth(locationString);
                if (fullWidth < maxWidth) {
                    SpeedSearchUtil.appendColoredFragmentForMatcher(locationString, renderer, SimpleTextAttributes.GRAYED_ATTRIBUTES, itemMatchers.nameMatcher, bgColor, selected);
                } else {
                    final int adjustedWidth = Math.max(locationString.length() * maxWidth / fullWidth - 1, 3);
                    locationString = StringUtil.trimMiddle(locationString, adjustedWidth);
                    SpeedSearchUtil.appendColoredFragmentForMatcher(locationString, renderer, SimpleTextAttributes.GRAYED_ATTRIBUTES, itemMatchers.nameMatcher, bgColor, selected);
                }
                return true;
            }
        };
    }
    
    @Nullable
    public Object getDataForItem(@NotNull final ProjectManagerNavigationItem element, @NotNull final String dataId) {
        return null;
    }
    
    public boolean processSelectedItem(@NotNull final ProjectManagerNavigationItem selected, final int modifiers, @NotNull final String searchText) {
        if (selected.isOpen()) {
            activeProject(this.event, selected.getProjectPath());
            return true;
        }

        openProject(selected, this.event);
        /*ApplicationManager.getApplication().invokeLater(() -> {
            try {
                openProject(selected, this.event);
            }catch (Exception e){

            }
        });*/

        return true;
    }

    private static void invokeLaterIfNeeded(@NotNull Project project, @NotNull Runnable runnable) {
        if (project.isDefault()) {
            return;
        }

        StartupManagerEx.getInstanceEx(project).runAfterOpened(() -> {
            ModalityUiUtil.invokeLaterIfNeeded(ModalityState.defaultModalityState(), project.getDisposed(), runnable);
        });
    }
    
    public static void openProject(final ProjectManagerNavigationItem selected, @NotNull final AnActionEvent event) {
        final Project project = event.getRequiredData(CommonDataKeys.PROJECT);
        final String projectBasePath = selected.getProjectPath();
        final Path path = Paths.get(projectBasePath).normalize();
        if (Files.isDirectory(path)) {
            ApplicationManager.getApplication().invokeLater(() -> {
                try {
                    OpenProjectTask openProjectTask = OpenProjectTask.build().withProjectToClose(null);
                    Project openedProject = ProjectUtil.openOrImport(path, openProjectTask);
                    if (openedProject != null){
                        FileChooserUtil.setLastOpenedFile(openedProject, path);
                    }
                }catch (Exception e){

                }
            });
        } else if (Messages.showDialog(project, IdeBundle.message("message.the.path.0.does.not.exist.maybe.on.remote", new Object[] { FileUtil.toSystemDependentName(projectBasePath) }), IdeBundle.message("dialog.title.reopen.project", new Object[0]), new String[] { CommonBundle.getOkButtonText(), IdeBundle.message("button.remove.from.list", new Object[0]) }, 0, Messages.getErrorIcon()) == 1) {
            RecentProjectsManager.getInstance().removePath(projectBasePath);
            //ProgressManager.getInstance().run( new ProjectInitTask(project, MyProjectSwitcherSetting.getInstance().getProjectDirectoryList()));
        }

    }

    public static void activeProject(@NotNull final AnActionEvent event, final String projectLocation) {
        final ActionManager actionManager = ActionManager.getInstance();
        final AnAction action = actionManager.getAction("OpenProjectWindows");
        if (action instanceof ProjectWindowActionGroup) {
            ProjectWindowActionGroup projectWindowActionGroup = (ProjectWindowActionGroup)action;
            AnAction[] children = projectWindowActionGroup.getChildren(event); //actionManager
            for (AnAction child : children) {
                if (!( child instanceof ProjectWindowAction)) {
                    continue;
                }

                ProjectWindowAction windowAction = (ProjectWindowAction)child;
                if (projectLocation.equals(windowAction.getProjectLocation())) {
                    windowAction.setSelected(event, true);
                    break;
                }
            }
        }
    }

    public static void closeProject(final ProjectManagerNavigationItem selectedItem, @NotNull final AnActionEvent event) {
        if (!selectedItem.isOpen()) {
            return;
        }
        final CloseProjectsActionBase closeProjectsActionBase = new CloseProjectsActionBase() {
            protected boolean shouldShow(@NotNull final AnActionEvent anActionEvent) {
                return false;
            }

            protected boolean canClose(@NotNull final Project project, @NotNull final Project currentProject) {
                return selectedItem.getProjectPath().equals(project.getPresentableUrl());
            }
        };
        closeProjectsActionBase.actionPerformed(event);
        IdeEventQueue.getInstance().getPopupManager().closeAllPopups(false);
    }

    @Override
    public boolean isEmptyPatternSupported() {
        return true;
    }

    @Override
    public boolean isShownInSeparateTab() {
        return true;
    }

    @Override
    public boolean showInFindResults() {
        return false;
    }

    @Override
    public void fetchWeightedElements(@NotNull final String pattern, @NotNull final ProgressIndicator progressIndicator, final @NotNull Processor<? super FoundItemDescriptor<ProjectManagerNavigationItem>> consumer) {
        if (myProject == null) return;

        final SearchEverywhereManager seManager = SearchEverywhereManager.getInstance(myProject);
        if (!this.getSearchProviderId().equals(seManager.getSelectedTabID())) {
            return;
        }

        if (this.foundItemDescriptorList == null){
            foundItemDescriptorList = getFoundItemDescriptors();
        }

        final MinusculeMatcher minusculeMatcher = NameUtil.buildMatcher("*" + pattern + "*", NameUtil.MatchingCaseSensitivity.NONE);
        for (FoundItemDescriptor<ProjectManagerNavigationItem> foundItemDescriptor : foundItemDescriptorList) {
            ProjectManagerNavigationItem navigationItem = foundItemDescriptor.getItem();
            if ((minusculeMatcher.matches(navigationItem.getProjectName()) || minusculeMatcher.matches(navigationItem.getProjectPath())) && !consumer.process(foundItemDescriptor)) {
                return;
            }
        }

    }

    private static List<FoundItemDescriptor<ProjectManagerNavigationItem>> getFoundItemDescriptors() {
        List<FoundItemDescriptor<ProjectManagerNavigationItem>> foundItemDescriptorList = new ArrayList<>(16);

        //已经打开的项目
        final Project[] openProjects = ProjectUtil.getOpenProjects();
        for (final Project openProject : openProjects) {
            final ProjectManagerNavigationItem navigationItem = new ProjectManagerNavigationItem(openProject.getName(), openProject.getPresentableUrl(), true);
            FoundItemDescriptor<ProjectManagerNavigationItem> descriptor = new FoundItemDescriptor<>(navigationItem, 1);
            foundItemDescriptorList.add(descriptor);
        }

        //最近打开项目列表中
        final Set<String> filterProjectPathSet = Arrays.stream(openProjects).map(Project::getPresentableUrl).collect(Collectors.toSet());
        final RecentProjectsManagerBase recentProjectsManagerBase = (RecentProjectsManagerBase)RecentProjectsManager.getInstance();
        final List<String> recentPaths = recentProjectsManagerBase.getRecentPaths();
        for (final String recentPath : recentPaths) {
            if (!filterProjectPathSet.contains(recentPath)) {
                String projectName = recentProjectsManagerBase.getProjectName(recentPath);
                filterProjectPathSet.add(recentPath);

                ProjectManagerNavigationItem navigationItem = new ProjectManagerNavigationItem(projectName, recentPath, false);
                FoundItemDescriptor<ProjectManagerNavigationItem> descriptor = new FoundItemDescriptor<>(navigationItem, 0);
                foundItemDescriptorList.add(descriptor);
            }
        }

        //ProjectSwitcher 的项目列表
        final TreeSet<ProjectDirectory> projectDirectorys = ProjectManagerSetting.getInstance().getProjectDirectorys();
        for (ProjectDirectory projectDirectory : projectDirectorys) {
            List<ProjectInfo> projects = projectDirectory.getProjects();
            List<FoundItemDescriptor<ProjectManagerNavigationItem>> recent = projects.stream()
                    .map(projectInfo -> new ProjectManagerNavigationItem(projectInfo.getName(), projectInfo.getPath()))
                    .filter(navigationItem -> !filterProjectPathSet.contains(navigationItem.getProjectPath()))
                    .map(navigationItem -> new FoundItemDescriptor<>(navigationItem, 0))
                    .collect(Collectors.toList());
            foundItemDescriptorList.addAll(recent);
        }
        return foundItemDescriptorList;
    }

    @Override
    public boolean isDumbAware() {
        return false;
    }

    public static class Factory implements SearchEverywhereContributorFactory<ProjectManagerNavigationItem> {

        @NotNull
        public SearchEverywhereContributor<ProjectManagerNavigationItem> createContributor(@NotNull final AnActionEvent initEvent) {
            return new ProjectManagerEverywhereContributor(initEvent);
        }
    }
}
